让我们看一下Spring如何定义关键的切点的概念。
Spring的切点模型可以让切点在不同的通知类型中重用。你可以为不同的通知绑定相同的切点。
org.springframework.aop.Pointcut
就是核心的接口,用来为特定的类和方法设置通知。下面就是这个接口:
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
Pointcut
接口分为两部分,分别用来对类和方法匹配,这两部分可以细粒度的组合(比如让两个结合使用)。
ClassFilter
接口将切点限制在特定的目标类上。如果matcher()
方法总是返回true,那么就是匹配所有的类:
public interface ClassFilter {
boolean matches(Class clazz);
}
MethodMatcher
接口通常更重要。完整的接口如下:
public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}
matches(Method, Class)
方法用来测试切点是否匹配目标类上的特定方法。可以在AOP代理创建的时候执行这个评估,避免每次方法调用时都需要测验。如果对于给定的方法,2个参数的matches方法返回了true,并且MethodMatcher的isRuntime()
返回了true,那么每次方法调用时3个参数的mathes方法会被调用。这使得切点可以在通知执行前立即查看传递给方法的参数。
大多数MethodMatcher是静态的,这意味他们的isRuntime()
方法是返回false。在这种情况下,3个参数的matches方法将永远不会被调用。
如果可能,尽量让pointcuts是静态的,AOP框架会在AOP代理创建的时候缓存切点评估的结果。
Spring支持对切点的操作:特别熟,union和intersection。
Union意味着需要匹配至少一个切点。
Intersection以为这需要匹配全部的切点。
Union通常更有用。
切点可以由org.springframework.aop.support.Pointcuts
类中的静态方法组成,或是用同一个包下的ComposablePointcut类。然而,使用AspectJ切点表达式通常是更简单的方式。
从2.0起,Spring使用的最主要的切点类型就是org.springframework.aop.aspectj.AspectJExpressionPointcut
。它用AspectJ提供的库解析AspectJ切点表达式字符串。
有关支持AspectJ切点的讨论,请看上一章。
Spring提供了一些便利的pointcut的实现。一些可以拿来直接使用;另一些被用来子类化成特定应用的切点。
静态的切点是基于方法和目标累的,无法考虑方法的参数。大多是情况下,静态的切点足够了,并且也是最好的选择。静态的切点能只被Spring评估一次,在一个方法第一次被调用时:在这之后,每次方法被调用时不再需要评估。
让我们看一些Spring内部的一些静态pointcut的实现。
指定静态切点很常见的方法是用正则表达式。Spring之外的一些AOP框架实现了这一点。
org.springframework.aop.support.JdkRegexpMethodPointcut
是一个常用的正则表达式切点,使用了JDK1.4+对正则表达式提供的支持。
使用JdkRegexpMethodPointcut
时,你可以设置字符串样式列表。如果列表中有一个匹配成功,切点的评估就会返回true
。(因此这是对这些切点的有效的union。)
用法如下:
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
Spring提供了一个方便的类,RegexpMethodPointcutAdvisor
,允许我们引用一个通知(请记住通知是一个拦截器,before advice,throws advice等)。本质上,还是使用了JdkRegexpMethodPointcut
。使用RegexpMethodPointcutAdvisor
把切点和通知封装到了一个bean中,简化了二者的联系,像下面这样:
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
RegexpMethodPointcutAdvisor适用于任何类型的通知。
一个重要的静态切点的类型是metadata-driven切点。它使用了元数据的属性值:通常是源码级别的元数据。
动态切点的评估过程比静态的花费要高。它们除了静态信息之外,还可以考虑方法的参数。这意味每次方法调用的时候他们都需要评估;因为参数是变化的,所以结果无法被缓存。
最主要的例子就是control flow
切点。
Spring的control flow切点和AspectJ的cflow切点的概念是相似的,虽然它的功能较弱。(目前没有办法指定一个切点在另一个切点匹配的连接点下执行。)一个control flow切点会匹配当前的调用栈。比如,当一个连接点被com.mycompany.web.package
包的一个方法调用或是被SomeCaller
类调用,可能会触发它。Control flow切点需要用org.springframework.aop.support.ControllerFlowPointcut
类。
与其他动态切点相比,Control flow切点在运行时评估的代价更巨大。在Java1.4中,它的花费大约是其他动态切点的五倍。
Spring提供了有用的pointcut父类来帮你实现自己的切点。
由于静态切点是最常用的,你可以像下列这样子类化StaticMethodMatcherPointcut
。这只需要实现一个抽象方法(尽管重写其他方法也是可行的):
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
Spring也提供了动态切点的父类。
你可以在Spring 1.0 RC2及更高版本中使用任何通知类型的自定义切点。
由于Spring AOP中的切点是Java类,而不是其他语言的特性(像AspectJ)。无论是静态还是动态的,都可以声明自定义的切点。Spring中自定义的切点任意的复杂。但是,如果可能的话还是建议使用AspectJ切点表达式。
之后版本的Spring也许会提供“语义切入点”,像JAC所提供的:例如,“目标对象中所有更改实例变量的所有方法。”